import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Random;

import junit.framework.TestCase;
import edu.uwm.cs552.HexCoordinate;
import edu.uwm.cs552.HexDirection;
import edu.uwm.cs552.HexEdge;
import edu.uwm.cs552.util.Pair;



public class TestHexEdge extends TestCase {

	protected <T> void testOneOf(T val, T... possible) {
		for (T poss : possible) {
			if (poss == null ? val == null : poss.equals(val)) {
				assertTrue(true);
				return;
			}
		}
		assertFalse(val + " not a member of " + Arrays.toString(possible), true);
	}
	
	// Normally, it's not a good idea to test equality with double values,
	// because 0.999999999999 is not the same as 1.0.
	// But in this case, we are using fractions (0.5) that are exactly
	// representable.
	
	public void testNE() {
		HexEdge e = new HexEdge(new HexCoordinate(3,1),HexDirection.NORTHEAST);
		assertEquals(3.0,e.a());
		assertEquals(0.5,e.b());
		testOneOf(e.toString(),"<3,1>@NORTHEAST","<3.0>@SOUTHWEST");
	}
	
	public void testE() {
		HexEdge e = new HexEdge(new HexCoordinate(4,3),HexDirection.EAST);
		assertEquals(4.5,e.a());
		assertEquals(3.0,e.b());
		testOneOf(e.toString(),"<4,3>@EAST","<5,3>@WEST");
	}
	
	public void testSE() {
		HexEdge e = new HexEdge(new HexCoordinate(2,2),HexDirection.SOUTHEAST);
		assertEquals(2.5,e.a());
		assertEquals(2.5,e.b());
		testOneOf(e.toString(),"<2,2>@SOUTHEAST","<3,3>@NORTHWEST");
	}
	
	public void testSW() {
		HexEdge e = new HexEdge(new HexCoordinate(1,2),HexDirection.SOUTHWEST);
		assertEquals(1.0,e.a());
		assertEquals(2.5,e.b());
		testOneOf(e.toString(),"<1,2>@SOUTHWEST","<1,3>@NORTHEAST");
	}
	
	public void testW() {
		HexEdge e = new HexEdge(new HexCoordinate(1,1),HexDirection.WEST);
		assertEquals(0.5,e.a());
		assertEquals(1.0,e.b());
		testOneOf(e.toString(),"<1,1>@WEST","<0,1>@EAST");
	}
	
	public void testNW() {
		HexEdge e = new HexEdge(new HexCoordinate(0,0), HexDirection.NORTHWEST);
		assertEquals(-0.5,e.a());
		assertEquals(-0.5,e.b());
		testOneOf(e.toString(),"<0,0>@NORTHWEST","<-1,-1>@SOUTHEAST");
	}
	
	public void testFromString1() {
		HexEdge e = HexEdge.fromString("<3,2>@WEST");
		assertEquals(2.5,e.a());
		assertEquals(2.0,e.b());
	}
	
	public void testFromString2() {
		HexEdge e = HexEdge.fromString("<0,-1>@NORTHWEST");
		assertEquals(-0.5,e.a());
		assertEquals(-1.5,e.b());
	}
	
	public void testEqualsNESW() {
		HexEdge e1a = new HexEdge(new HexCoordinate(3,1), HexDirection.NORTHEAST);
		HexEdge e1b = new HexEdge(new HexCoordinate(3,1), HexDirection.NORTHEAST);
		HexEdge e1c = new HexEdge(new HexCoordinate(3,0), HexDirection.SOUTHWEST);
		HexEdge e2 = new HexEdge(new HexCoordinate(3,0), HexDirection.NORTHEAST);
		HexEdge e3 = new HexEdge(new HexCoordinate(3,1), HexDirection.SOUTHWEST);
		
		assertTrue(e1a.equals(e1a));
		assertTrue(e1a.equals(e1b));
		assertTrue(e1a.equals(e1c));
		assertFalse(e1a.equals(e2));
		assertFalse(e1a.equals(e3));
		
		assertTrue(e1c.equals(e1b));
		
		assertFalse(e2.equals(e1a));
		assertFalse(e2.equals(e1c));
		assertFalse(e3.equals(e1b));
		assertFalse(e3.equals(e1c));
		
		assertFalse(e2.equals(e3));
	}
	
	public void testEqualsEW() {
		HexEdge e1a = new HexEdge(new HexCoordinate(2,2), HexDirection.EAST);
		HexEdge e1b = new HexEdge(new HexCoordinate(2,2), HexDirection.EAST);
		HexEdge e1c = new HexEdge(new HexCoordinate(3,2), HexDirection.WEST);
		HexEdge e2 = new HexEdge(new HexCoordinate(3,2), HexDirection.EAST);
		HexEdge e3 = new HexEdge(new HexCoordinate(2,2), HexDirection.WEST);
		
		assertTrue(e1a.equals(e1a));
		assertTrue(e1a.equals(e1b));
		assertTrue(e1a.equals(e1c));
		assertFalse(e1a.equals(e2));
		assertFalse(e1a.equals(e3));
		
		assertTrue(e1c.equals(e1b));
		
		assertFalse(e2.equals(e1a));
		assertFalse(e2.equals(e1c));
		assertFalse(e3.equals(e1b));
		assertFalse(e3.equals(e1c));
		
		assertFalse(e2.equals(e3));
	}
	
	public void testEqualsSENW() {
		HexEdge e1a = new HexEdge(new HexCoordinate(2,2), HexDirection.EAST);
		HexEdge e1b = new HexEdge(new HexCoordinate(2,2), HexDirection.EAST);
		HexEdge e1c = new HexEdge(new HexCoordinate(3,2), HexDirection.WEST);
		HexEdge e2 = new HexEdge(new HexCoordinate(3,2), HexDirection.EAST);
		HexEdge e3 = new HexEdge(new HexCoordinate(2,2), HexDirection.WEST);
		
		assertTrue(e1a.equals(e1a));
		assertTrue(e1a.equals(e1b));
		assertTrue(e1a.equals(e1c));
		assertFalse(e1a.equals(e2));
		assertFalse(e1a.equals(e3));
		
		assertTrue(e1c.equals(e1b));
		
		assertFalse(e2.equals(e1a));
		assertFalse(e2.equals(e1c));
		assertFalse(e3.equals(e1b));
		assertFalse(e3.equals(e1c));
		
		assertFalse(e2.equals(e3));		
	}
	
	private static final int SEED = 1776;
	private static final int TESTS = 100;
	private static final int RANGE = 10;
	
	private HexEdge randomEdge(Random r) {
		return new HexEdge(new HexCoordinate(r.nextInt(RANGE),r.nextInt(RANGE)),HexDirection.values()[r.nextInt(6)]);
	}
	
	public void testRandom() {
		Random r = new Random(SEED);
		Collection<HexEdge> c = new ArrayList<HexEdge>();
		for (int i=0; i < TESTS; ++i) {
			c.add(randomEdge(r));
		}
		for (HexEdge e1 : c) {
			assertEquals(e1,HexEdge.fromString(e1.toString()));
			for (HexEdge e2 : c) {
				if (e1.a() == e2.a() && e1.b() == e2.b()) {
					// if (e1 != e2) System.out.println("Found equal " + e1 + " and " + e2);
					assertEquals(e1 + " == " + e2, e1, e2);
					assertEquals(e1 + ".hash = " + e1.hashCode() + " == " + e2 + ".hash = " + e2.hashCode(),
							e1.hashCode(),e2.hashCode());
				} else {
					assertFalse(e1 + " != " + e2, e1.equals(e2));
					assertFalse(e1 + ".hash = " + e1.hashCode() + " != " + e2 + ".hash = " + e2.hashCode(),
							e1.hashCode() == e2.hashCode());
				}
			}
		}
	}
	
	private void testLineSegment(Pair<Point,Point> pp, Point p1, Point p2) {
		assertFalse(pp.fst.equals(pp.snd));
		testOneOf(pp.fst,p1,p2);
		testOneOf(pp.snd,p1,p2);
	}
	
	public void testLineEW() {
		HexEdge e1 = new HexEdge(new HexCoordinate(3,1),HexDirection.EAST);
		HexEdge e2 = new HexEdge(new HexCoordinate(4,1),HexDirection.WEST);
		testLineSegment(e1.toLineSegment(10),new Point(30,6),new Point(30,12));
		testLineSegment(e2.toLineSegment(10),new Point(30,6),new Point(30,12));
	}
	
	public void testLineNESW() {
		HexEdge e1 = new HexEdge(new HexCoordinate(2,2),HexDirection.NORTHEAST);
		HexEdge e2 = new HexEdge(new HexCoordinate(2,1),HexDirection.SOUTHWEST);
		testLineSegment(e1.toLineSegment(10),new Point(10,12),new Point(15,14));
		testLineSegment(e2.toLineSegment(10),new Point(10,12),new Point(15,14));
	}
	
	public void testLineSENW() {
		HexEdge e1 = new HexEdge(new HexCoordinate(6,3),HexDirection.SOUTHEAST);
		HexEdge e2 = new HexEdge(new HexCoordinate(7,4),HexDirection.NORTHWEST);
		testLineSegment(e1.toLineSegment(100),new Point(450,318), new Point(500,289));
		testLineSegment(e2.toLineSegment(100),new Point(450,318), new Point(500,289));
	}
}
